package com.jas.common;
//@author A0098803W
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//@author A0098803W
/**
 * This class handles common operations for all the Java classes in this
 * program.
 * 
 * 
 * 
 */
public final class Utilities {

	//@author A0098803W
	/**
	 * This method returns a Calendar object for the current date/time.
	 * 
	 */
	public static Calendar getCurrentDate() {
		Calendar today = Calendar.getInstance();
		return today;
	}

	//@author A0098803W
	/**
	 * This method sorts the list of events based on the sorting order specified
	 * in the parameter. Either <code>asc</code> for ascending and
	 * <code>desc</code> for descending.
	 * 
	 * @param eventList
	 *            The event list to sort.
	 * @param sortOrder
	 *            Either <code>asc</code> or <code>desc</code>
	 * @return A sorted list based on the sorting order specified in the
	 *         parameter.
	 */
	public static ArrayList<Event> sortList(ArrayList<Event> eventList,
			String sortOrder) {
		assert (sortOrder.equalsIgnoreCase("asc") || sortOrder
				.equalsIgnoreCase("desc"));
		Collections.sort(eventList, new EventDateComparator(sortOrder));

		return eventList;
	}

	//@author A0098803W
	/**
	 * Since a small terminal will distort program command line display, this
	 * method checks if this platform is running on Windows and checks current
	 * terminal size.
	 * 
	 */
	public static void checkMicrosoftTerminalSize() {
		try {

			String currentOperatingSystem = Utilities.getOperatingSystemName()
					.toLowerCase();

			if (currentOperatingSystem.contains("95")) {
				throw new UnsupportedOperationException(
						Language.getString("ERROR_UNSUPPORTED_OPERATING_SYSTEM"));
			}
			if (currentOperatingSystem.contains("win")) {

				String[] cmd = new String[3];

				cmd[0] = "cmd.exe";
				cmd[1] = "/C";
				cmd[2] = "MODE";

				Process proc = new ProcessBuilder(cmd).start();

				InputStreamReader isr = new InputStreamReader(
						proc.getInputStream());
				BufferedReader br = new BufferedReader(isr);
				String line = null;
				int currentTerminalSize = 0;
				while ((line = br.readLine()) != null) {
					String checkForColumnSize = line.toLowerCase();
					checkForColumnSize = checkForColumnSize.trim();
					if (checkForColumnSize.contains("col")) {
						String[] splittedParameters = checkForColumnSize
								.split(":");
						splittedParameters[1] = splittedParameters[1].trim();
						currentTerminalSize = Integer
								.parseInt(splittedParameters[1]);

					}

				}

				int getMinimumColumnDisplaySize = Integer.parseInt(Language
						.getString("MINIMUM_COLUMN_SIZE_FOR_WINDOWS"));

				if (currentTerminalSize < getMinimumColumnDisplaySize) {
					throw new IOException(
							Language.getString("ERROR_DISPLAY_TERMINAL_TOO_SMALL"));
				}

			} else {
				throw new IOException(
						Language.getString("ERROR_DISPLAY_TERMINAL_TOO_SMALL"));
			}

		} catch (IOException e) {
			System.out.println(e.getMessage());

		} catch (UnsupportedOperationException e) {
			System.out.println(e.getMessage());

		}
	}

	//@author A0098803W
	/**
	 * This method returns the default storage file name for the program.
	 * 
	 * @return Returns text of the default storage file name for the program.
	 */
	public static String getDefaultFileName() {
		return Utilities.getUserHomeDirectory() + File.separator
				+ Language.getString("JSON_FILE_NAME") + ".json";
	}
	
		//@author A0098803W
		/**
		 * This method returns the default log file name for the program.
		 * 
		 * @return Returns text of the default storage file name for the program.
		 */
		public static String getDefaultLogFileName() {
			return Utilities.getUserHomeDirectory() + File.separator
					+ Language.getString("LOG_FILE_NAME") + ".txt";
		}

	//@author A0098803W
	/**
	 * This method returns the default message gateway file name for the
	 * program.
	 * 
	 * @return Returns text of the default message gateway file name for the
	 *         program.
	 */
	public static String getDefaultGatewayFileName() {
		return Utilities.getUserHomeDirectory() + File.separator
				+ Language.getString("JSON_GATEWAY_FILE_NAME") + ".json";

	}

	// @author A0098803W
	/**
	 * This method returns the default notification list file name for the
	 * program.
	 * 
	 * @return Returns text of the default notification list file name for the
	 *         program.
	 */
	public static String getDefaultNotificationFileName() {
		return Utilities.getUserHomeDirectory() + File.separator
				+ Language.getString("JSON_NOTIFICATION_FILE_NAME") + ".json";

	}

	//@author A0098803W
	/**
	 * This method returns the name of the user's current operating system.
	 * 
	 * @return Returns user's operating system. For example Windows, Linux, etc.
	 */
	public static String getOperatingSystemName() {
		return System.getProperty("os.name");
	}

	//@author A0098803W
	/**
	 * This method returns the version of the user's current operating system.
	 * 
	 * @return Returns user's operating system version.
	 */
	public static String getOperatingSystemVersion() {
		return System.getProperty("os.version");
	}

	//@author A0098803W
	/**
	 * This method returns the user's current home directory.
	 * 
	 * @return Returns user's current home directory.
	 */
	public static String getUserHomeDirectory() {
		return System.getProperty("user.home");
	}

	//@author A0098803W
	/**
	 * This method returns a unique ID generated using Java <code>UUID</code>
	 * class.
	 * 
	 * @return Returns a unique ID.
	 */
	public static String generateUniqueIdentification() {
		return GenerateShortUUID.next();
	}

	//@author a0118677r
	/**
	 * This method converts a <code>Calendar</code> object to a
	 * <code>String</code> format using the format specified in the parameters.
	 * 
	 * @param calendar
	 *            The <code>Calendar</code> object to convert.
	 * @param format
	 *            The format to use when converting.
	 * @return A <code>String</code> representation of the <code>Calendar</code>
	 *         object.
	 */
	public static String calendarToString(Calendar calendar, String format) {
		SimpleDateFormat dateFormat = new SimpleDateFormat(format);

		Date date = calendar.getTime();
		return dateFormat.format(date);
	}

	//@author a0118677r
	/**
	 * This method converts a <code>String</code> text to a
	 * <code>Calendar</code> object using the format specified in the
	 * parameters.
	 * 
	 * @param dateTimeString
	 *            The <code>String</code> text to convert.
	 * @param format
	 *            The format to use when converting.
	 * @return A <code>Calendar</code> object.
	 * @throws DateFormatException
	 *             Throws an exception if the input <code>String</code> is not
	 *             following the given format.
	 */
	public static Calendar stringToCalendar(String dateTimeString, String format)
			throws DateFormatException {
		SimpleDateFormat dateFormat = new SimpleDateFormat(format);

		try {
			Date date = dateFormat.parse(dateTimeString);
			GregorianCalendar calendar = new GregorianCalendar();
			calendar.setTime(date);
			return calendar;
		} catch (ParseException e) {
			throw new DateFormatException(
					Language.getString("ERROR_WRONG_DATE_FORMAT"));
		}
	}

	//@author a0118677r
	public static boolean isDateTimeString(String dateTimeString,
			String dateFormat) {
		try {
			stringToCalendar(dateTimeString, dateFormat);
			return true;
		} catch (DateFormatException e) {
			return false;
		}
	}

	//@author A0098803W
	/**
	 * This method checks the user input date and searches for the format
	 * currently used for this input.
	 * 
	 * @param searchText
	 *            User's date input.
	 * @return Returns the text format used for the date or an empty text if no
	 *         such format can be found. Valid date format example: <br>
	 * <br>
	 *         <code>dd/MM/yyyy</code>
	 */
	public static String getDateFormat(String searchText) {
		searchText = searchText.trim();
		String format = "";
		boolean isValidSearchDate = Utilities.isValidDateForSearch(searchText,
				Language.getString("SEARCH_DATE_REGEX"));

		boolean isValidSearchDateTime = Utilities.isValidDateForSearch(
				searchText, Language.getString("SEARCH_DATE_TIME_REGEX"));
		boolean isValidSearchMonth = Utilities.isValidDateForSearch(searchText,
				Language.getString("SEARCH_MONTH_REGEX"));
		boolean isValidSearchYearForShowCommand = Utilities
				.isValidDateForSearch(searchText, Language
						.getString("SEARCH_YEAR_REGEX_FOR_SHOW_COMMAND"));
		if (isValidSearchDate) {
			return format = Language.getString("SEARCH_DATE_FORMAT");
		} else if (isValidSearchDateTime) {

			return format = Language.getString("SEARCH_DATE_TIME_FORMAT");
		} else if (isValidSearchMonth) {

			return format = Language.getString("SEARCH_MONTH_FORMAT");

		} else if (isValidSearchYearForShowCommand) {

			return format = Language.getString("SEARCH_YEAR_FORMAT");
		}

		return format;

	}
	
	
	//@author A0098803W
		/**
		 * This method checks the user URL input for the message gateway.
		 * 
		 * @param searchText
		 *            User's URL input.
		 * @return Returns the URL after verification or an empty text if URL is invalid. Valid URL format example: <br>
		 * <br>
		 *         <code>http://www.yahoo.com</code>
		 */
		public static String validateGatewayURL(String searchText) {
			searchText = searchText.trim();
			Pattern pattern = Pattern.compile(Language.getString("VALIDATE_URL_REGEX"));
			Matcher matcher = pattern.matcher(searchText);			
			String format = "";
		
			if (matcher.matches()) {
				return searchText;
			} 

			return format;

		}

	//@author A0098803W
	/**
	 * This method checks if the search text is using the date format (MM/yyyy).
	 * 
	 * @param date
	 *            The search text entered by the user.
	 * @param format
	 *            The format used to compare.
	 * @return Returns <code>true</code> if the search text is valid and
	 *         <code>false</code> if otherwise.
	 */
	public static boolean isValidDateForSearch(final String date, String format) {
		Pattern pattern = Pattern.compile(format);
		Matcher matcher = pattern.matcher(date);

		if (matcher.matches()) {

			matcher.reset();

			if (matcher.find()) {

				String day = matcher.group(1);
				String month = matcher.group(2);

				int year;
				try {
					year = Integer.parseInt(matcher.group(3));
				} catch (IndexOutOfBoundsException e) {
					year = Integer.parseInt(matcher.group(1));
				}
				if (day.equals("31")
						&& (month.equals("4") || month.equals("6")
								|| month.equals("9") || month.equals("11")
								|| month.equals("04") || month.equals("06") || month
									.equals("09"))) {
					return false; // only 1,3,5,7,8,10,12 has 31 days
				} else if (month.equals("2") || month.equals("02")) {
					// leap year
					if (year % 4 == 0) {
						if (day.equals("30") || day.equals("31")) {
							return false;
						} else {
							return true;
						}
					} else {
						if (day.equals("29") || day.equals("30")
								|| day.equals("31")) {
							return false;
						} else {
							return true;
						}
					}
				} else {
					return true;
				}
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	//@author A0098803W
	/**
	 * This method checks for spaces present in user's command execution.
	 * 
	 * @param userText
	 *            User text
	 * @return
	 */
	public static boolean isWhiteSpaceFound(String userText) {
		Pattern whiteSpacePattern = Pattern.compile("\\s");
		Matcher matchWhiteSpace = whiteSpacePattern.matcher(userText);
		boolean isWhitespaceFound = matchWhiteSpace.find();
		if (isWhitespaceFound) {
			return true;
		}
		return false;
	}

	//@author A0098803W
	/**
	 * This method extracts the sequence of numbers entered by the user. For
	 * example 1-4,6,7 will return a list containing 1,2,3,4,6,7
	 * 
	 * @param userText
	 *            The sequence text entered by the user.
	 * @return Returns the extracted sequence or an empty list if none
	 *         extracted.
	 * @throws CommandFormatException
	 */
	public static ArrayList<String> splitSequenceRange(String userText)
			throws CommandFormatException {
		if (isWhiteSpaceFound(userText)) {
			throw new CommandFormatException(
					Language.getString("ERROR_WHITESPACES_IN_SEQUENCE_RANGE"));
		}
		userText = userText.trim();
		Pattern pattern = Pattern
				.compile("(?!([1-9]{1}[0-9]*)-(([1-9]{1}[0-9]*))-)^(([1-9]{1}[0-9]*)|(([1-9]{1}[0-9]*)(,|-|(,?([1-9]{1}[0-9]*))|(-?([1-9]{1}[0-9]*)){1})*))$");
		Matcher matcher = pattern.matcher(userText);

		ArrayList<String> results = new ArrayList<String>();
		if (matcher.matches()) {

			matcher.reset();
			String[] splittedParameters = userText.split(",");
			for (int i = 0; i < splittedParameters.length; i++) {
				int checkForDash = splittedParameters[i].indexOf("-");
				if (checkForDash != -1) {
					int numberBeforeDash = Integer
							.parseInt(splittedParameters[i].substring(0,
									checkForDash));
					int numberAfterDash = Integer
							.parseInt(splittedParameters[i].substring(
									checkForDash + 1,
									splittedParameters[i].length()));
					for (int j = numberBeforeDash; j < numberAfterDash + 1; j++) {
						results.add(String.valueOf(j));
					}
				} else {
					results.add(splittedParameters[i]);
				}
			}
		}
		return results;
	}

	//@author A0098803W
	/**
	 * 
	 * This method checks if the display serial number is valid.
	 * 
	 * @param list
	 *            The list to compare.
	 * @param mappingOfDisplayedIndexToEventId
	 *            The list of currently displayed serial numbers.
	 * @return Returns the <code>String</code> text of the invalid serial number
	 *         or an empty String.
	 */
	public static String checkValidDisplayIndex(ArrayList<String> list,
			TreeMap<Integer, String> mappingOfDisplayedIndexToEventId) {
		for (String displayedIndex : list) {
			Integer index = Integer.valueOf(displayedIndex);
			if (!mappingOfDisplayedIndexToEventId.containsKey(index)) {
				return displayedIndex;
			}
		}
		return "";
	}

}
